package com.piggy.logi.sys.service.impl;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.date.DateUtil;
import cn.hutool.core.util.ObjectUtil;
import cn.hutool.core.util.StrUtil;
import com.baomidou.mybatisplus.core.conditions.Wrapper;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.piggy.common.core.exception.GlobalException;
import com.piggy.common.core.utils.PageUtils;
import com.piggy.common.core.utils.sql.SqlUtil;
import com.piggy.common.core.web.page.PagePlus;
import com.piggy.common.core.web.page.TableDataInfo;
import com.piggy.logi.sys.bo.LogiWaybillPayableQueryBo;
import com.piggy.logi.sys.bo.LogiWaybillPayableRecordEditBo;
import com.piggy.logi.sys.domain.*;
import com.piggy.logi.sys.enums.SettleStatusEnum;
import com.piggy.logi.sys.enums.SettleTypeEnum;
import com.piggy.logi.sys.enums.WaybillStatusEnum;
import com.piggy.logi.sys.mapper.LogiWaybillPayableMapper;
import com.piggy.logi.sys.service.*;
import com.piggy.logi.sys.vo.LogiWaybillPayableVo;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import javax.annotation.Resource;
import java.math.BigDecimal;
import java.util.List;

/**
 * 应付管理Service业务层处理
 *
 * @author fengxy
 * @date 2023/10/17 21:54
 **/
@Slf4j
@Service
public class LogiWaybillPayableServiceImpl extends ServiceImpl<LogiWaybillPayableMapper, LogiWaybillPayable> implements ILogiWaybillPayableService {

    @Resource
    private ILogiWaybillService logiWaybillService;

    @Resource
    private ILogiWaybillSignService logiWaybillSignService;

    @Resource
    private ILogiWaybillFeeService logiWaybillFeeService;

    @Resource
    private ILogiCarrierService logiCarrierService;

    @Resource
    private ILogiWaybillPayableRecordService logiWaybillReceivableRecordService;

    @Resource
    private ILogiSysBaseConfigService logiSysBaseConfigService;

    @Override
    public TableDataInfo<LogiWaybillPayableVo> queryPageList(LogiWaybillPayableQueryBo bo) {
        PagePlus<LogiWaybillPayable, LogiWaybillPayableVo> pageVo = pageVo(PageUtils.buildPagePlus(bo, true), buildQueryWrapper(bo), LogiWaybillPayableVo.class);
        pageVo.getRecordsVo().forEach(this::fillData);
        return PageUtils.buildDataInfo(pageVo);
    }

    @Override
    public LogiWaybillPayableVo queryById(Long id) {
        LogiWaybillPayableVo payableVo = getVoById(id, LogiWaybillPayableVo.class);
        if (ObjectUtil.isNull(payableVo)) {
            throw new GlobalException("应付记录不存在");
        }
        fillData(payableVo);
        return payableVo;
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void confirmPay(LogiWaybillPayableRecordEditBo bo) {
        // 获取应付记录
        LogiWaybillPayable payable = getById(bo.getPayableId());
        if (ObjectUtil.isNull(payable)) {
            throw new GlobalException("应付记录不存在");
        }
        // 支付金额不能大于应付余额
        if (bo.getPayAmount().compareTo(payable.getPayableBalance()) > 0) {
            throw new GlobalException("支付金额不能大于应付余额");
        }
        // 新增付款记录
        LogiWaybillPayableRecord record = BeanUtil.toBean(bo, LogiWaybillPayableRecord.class);
        boolean success = logiWaybillReceivableRecordService.save(record);
        if (!success) {
            throw new GlobalException("新增付款记录失败");
        }
        // 增加实付金额、减少应付余额、付款记录数量+1
        success = update(Wrappers.lambdaUpdate(LogiWaybillPayable.class)
                .set(LogiWaybillPayable::getRealAmount, payable.getRealAmount().add(record.getPayAmount()))
                .set(LogiWaybillPayable::getPayableBalance,payable.getPayableBalance().subtract(record.getPayAmount()))
                .set(LogiWaybillPayable::getRecordNum, payable.getRecordNum() + 1)
                .eq(LogiWaybillPayable::getId, payable.getId()));
        if (!success) {
            throw new GlobalException("修改实付金额、应付余额、付款记录数量失败");
        }
    }

    @Override
    public List<LogiWaybillPayableRecord> payList(Long id) {
        return logiWaybillReceivableRecordService.list(Wrappers.lambdaQuery(LogiWaybillPayableRecord.class)
                .eq(LogiWaybillPayableRecord::getPayableId, id));
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void updateRecord(LogiWaybillPayableRecordEditBo bo) {
        // 校验参数
        if (ObjectUtil.isNull(bo.getId())) {
            throw new GlobalException("付款记录id为空");
        }
        // 查询老的付款记录
        LogiWaybillPayableRecord oldRecord = logiWaybillReceivableRecordService.getById(bo.getId());
        if (ObjectUtil.isNull(oldRecord)) {
            throw new GlobalException("付款记录不存在");
        }
        // 获取应付记录
        LogiWaybillPayable payable = getById(bo.getPayableId());
        if (ObjectUtil.isNull(payable)) {
            throw new GlobalException("应付记录不存在");
        }
        // 获取新的应付余额
        BigDecimal payableBalance = payable.getPayableBalance().add(oldRecord.getPayAmount()).subtract(bo.getPayAmount());
        // 应付余额不能小于0
        if (payableBalance.compareTo(BigDecimal.ZERO) < 0) {
            throw new GlobalException("应付余额不能小于0");
        }
        // 修改付款记录信息
        LogiWaybillPayableRecord newRecord = BeanUtil.toBean(bo, LogiWaybillPayableRecord.class);
        boolean success = logiWaybillReceivableRecordService.updateById(newRecord);
        if (!success) {
            throw new GlobalException("修改付款记录信息失败");
        }
        // 更新实付金额、应付余额
        success = update(Wrappers.lambdaUpdate(LogiWaybillPayable.class)
                .set(LogiWaybillPayable::getRealAmount, payable.getRealAmount().subtract(oldRecord.getPayAmount()).add(newRecord.getPayAmount()))
                .set(LogiWaybillPayable::getPayableBalance, payableBalance)
                .eq(LogiWaybillPayable::getId, payable.getId()));
        if (!success) {
            throw new GlobalException("修改实付金额、应付余额失败");
        }
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void deleteRecord(Long id) {
        // 查询付款记录
        LogiWaybillPayableRecord record = logiWaybillReceivableRecordService.getById(id);
        if (ObjectUtil.isNull(record)) {
            throw new GlobalException("付款记录不存在");
        }
        // 获取应付记录
        LogiWaybillPayable payable = getById(record.getPayableId());
        if (ObjectUtil.isNull(payable)) {
            throw new GlobalException("应付记录不存在");
        }
        // 删除付款记录
        boolean success = logiWaybillReceivableRecordService.removeById(id);
        if (!success) {
            throw new GlobalException("删除付款记录失败");
        }
        // 减少实付金额、增加应付余额、付款记录数量-1
        success = update(Wrappers.lambdaUpdate(LogiWaybillPayable.class)
                .set(LogiWaybillPayable::getRealAmount, payable.getRealAmount().subtract(record.getPayAmount()))
                .set(LogiWaybillPayable::getPayableBalance,payable.getPayableBalance().add(record.getPayAmount()))
                .set(LogiWaybillPayable::getRecordNum, payable.getRecordNum() - 1)
                .eq(LogiWaybillPayable::getId, payable.getId()));
        if (!success) {
            throw new GlobalException("修改实付金额、增加应付余额、付款记录数量失败");
        }
    }

    @Override
    public void confirmSettle(Long id) {
        // 查询应付信息
        LogiWaybillPayable payable = getById(id);
        if (ObjectUtil.isNull(payable)) {
            throw new GlobalException("应付记录不存在");
        }
        // 校验状态是否为未结清
        if (SettleStatusEnum.NOT_SETTLE.getCode() != payable.getStatus()) {
            throw new GlobalException("请选择未结清的应付记录");
        }
        // 校验应付余额是否为0
        LogiSysBaseConfig sysBaseConfig = logiSysBaseConfigService.getBaseConfig(payable.getDeptId());
        if (sysBaseConfig.getPayableSettled() == 1 && BigDecimal.ZERO.compareTo(payable.getPayableBalance()) != 0) {
            throw new GlobalException("应付余额不为0");
        }
        // 修改状态未：已结清
        boolean success = update(Wrappers.lambdaUpdate(LogiWaybillPayable.class)
                .set(LogiWaybillPayable::getStatus, SettleStatusEnum.SETTLED.getCode())
                .eq(LogiWaybillPayable::getId, payable.getId()));
        if (!success) {
            throw new GlobalException("修改结清状态状态已结清失败");
        }
    }

    @Override
    public void cancelSettle(Long id) {
        // 查询应付信息
        LogiWaybillPayable payable = getById(id);
        if (ObjectUtil.isNull(payable)) {
            throw new GlobalException("应付记录不存在");
        }
        // 判断状态是否为已结清
        if (SettleStatusEnum.SETTLED.getCode() != payable.getStatus()) {
            throw new GlobalException("请选择已结清的应付记录");
        }
        // 修改状态未：未结清
        boolean success = update(Wrappers.lambdaUpdate(LogiWaybillPayable.class)
                .set(LogiWaybillPayable::getStatus, SettleStatusEnum.NOT_SETTLE.getCode())
                .eq(LogiWaybillPayable::getId, payable.getId()));
        if (!success) {
            throw new GlobalException("修改结清状态状态未结清失败");
        }
    }

    @Override
    public void removeByUK(Long deptId, Long waybillId) {
        baseMapper.removeByUK(deptId, waybillId);
    }

    /**
     * 组装查询条件
     **/
    private Wrapper<LogiWaybillPayable> buildQueryWrapper(LogiWaybillPayableQueryBo bo) {
        LambdaQueryWrapper<LogiWaybillPayable> lqw = Wrappers.lambdaQuery();
        lqw.like(StrUtil.isNotBlank(bo.getOutCode()), LogiWaybillPayable::getOutCode, bo.getOutCode());
        lqw.eq(bo.getSettlementMode() != null, LogiWaybillPayable::getSettlementMode, bo.getSettlementMode());
        lqw.eq(bo.getStatus() != null, LogiWaybillPayable::getStatus, bo.getStatus());
        lqw.between(ObjectUtil.isAllNotEmpty(bo.getCreateStartDate(), bo.getCreateEndDate()), LogiWaybillPayable::getCreateTime, bo.getCreateStartDate(), bo.getCreateEndDate());
        // 承运商名称筛选
        if (StrUtil.isNotBlank(bo.getCarrierName())) {
            String existsSql = "select 1 from logi_carrier where logi_carrier.carrier_id = logi_waybill_payable.carrier_id" +
                    " and logi_carrier.carrier_name like '%" + bo.getCarrierName() + "%'";
            lqw.exists(existsSql);
        }
        SqlUtil.dataPermissionFilter(lqw, bo.getDeptId());
        return lqw;
    }

    /**
     * 补充字段数据
     **/
    private void fillData(LogiWaybillPayableVo payableVo) {
        // 设置承运商名称
        LogiCarrier carrier = logiCarrierService.getOne(Wrappers.lambdaQuery(LogiCarrier.class)
                .eq(LogiCarrier::getCarrierId, payableVo.getCarrierId()));
        payableVo.setCarrierName(ObjectUtil.isNotNull(carrier) ? carrier.getCarrierName() : "");
        // 设置运单状态
        LogiWaybill waybill = logiWaybillService.getById(payableVo.getWaybillId());
        payableVo.setWaybillStatus(waybill.getStatus());
        // 设置剩余时间（只有月结类型且运单状态为已签收之后的状态才有值）
        if (ObjectUtil.isNotNull(payableVo.getSettlementMode()) && payableVo.getSettlementMode() == SettleTypeEnum.ms.getCode()
                && payableVo.getWaybillStatus() >= WaybillStatusEnum.SIGNATURE.getCode()) {
            // 获取签收时间
            LogiWaybillSign waybillSign = logiWaybillSignService.getOne(Wrappers.lambdaQuery(LogiWaybillSign.class)
                    .eq(LogiWaybillSign::getWaybillId, payableVo.getWaybillId()));
            // 获取月结天数
            LogiWaybillFee waybillFee = logiWaybillFeeService.getOne(Wrappers.lambdaQuery(LogiWaybillFee.class)
                    .eq(LogiWaybillFee::getWaybillId, payableVo.getWaybillId()));
            // 计算剩余时间
            if (ObjectUtil.isNotNull(waybillSign.getUpdateTime()) && ObjectUtil.isNotNull(waybillFee.getMsDay())) {
                payableVo.setRemainDay(Math.toIntExact(waybillFee.getMsDay() - DateUtil.betweenDay(waybillSign.getUpdateTime(), DateUtil.date(), true)));
            }
        }
    }
}
